package cn.cimoc.service.impl;

import cn.cimoc.dto.AdminAddNewDTO;
import cn.cimoc.dto.AdminLoginDTO;
import cn.cimoc.ex.ServiceException;
import cn.cimoc.mapper.AdminMapper;
import cn.cimoc.model.Admin;
import cn.cimoc.util.GlobalPasswordEncoder;
import cn.cimoc.web.JsonResult;
import cn.cimoc.security.pojo.UserUserDetails;
import cn.cimoc.service.IAdminService;
import com.alibaba.fastjson.JSON;
import io.jsonwebtoken.Header;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;

@Service
@Slf4j
public class AdminServiceImpl implements IAdminService {
    //密钥 用于加密生成jwt数据
    @Value("${jwt.secret}")
    private String secretKey;
    @Value("${jwt.expiration}")
    private Long expiration;

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private AdminMapper adminMapper;
    @Autowired
    private GlobalPasswordEncoder globalPasswordEncoder;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void addNew(AdminAddNewDTO adminAddNewDTO) {
        //根据adminAddNewDTO中的username，
        // 调用AdminMapper的getByUsername(String username)执行查询，并获取查询结果
        String username = adminAddNewDTO.getUsername();
        Admin queryResult = adminMapper.getByUsername(username);
        log.debug("根据用户名查询到的结果：{}", queryResult);
        //判断返回值是否不为null
        if (queryResult != null) {
            //用户名已经被占用，抛出ServiceException异常
            log.warn("增加管理员失败，用户名{}已经被占用！", username);
            throw new ServiceException(JsonResult.State.ERR_CONFLICT, "增加管理员失败，用户名已经被占用！");
        }
        //开始执行新增
        //补全数据
        //创建新的admin对象，将新增的数据 封装在对象中
        Admin admin = new Admin();
        //为admin对象的属性赋值：username nickname来自于adminAddNewDTO的参数
        admin.setUsername(adminAddNewDTO.getUsername());
        admin.setNickname(adminAddNewDTO.getNickname());
        //为admin对象的属性赋值：password >密文密码
        String rawPassword = adminAddNewDTO.getPassword();
        String encodedPassword = globalPasswordEncoder.encode(rawPassword);
        admin.setPassword(encodedPassword);
        //为admin对象的属性赋值：avatar phone email description保持为null
        //为admin对象的属性赋值：isEnable = 1
        admin.setIsEnable(1);
        //为admin对象的属性赋值：lastLoginIp = null,loginCount=0,gmtLastLogin=null
        admin.setLoginCount(0);
        //为admin对象的属性赋值：gmtCreate,gmtModified 为当前时间
        LocalDateTime now = LocalDateTime.now();
        admin.setGmtCreate(now);
        admin.setGmtModified(now);
        log.debug("即将执行新增管理员的数据：{}", admin);
        int rows = adminMapper.addNew(admin);
        //判断返回值是否不为1
        if (rows != 1) {
            log.warn("服务器忙，请稍后再次尝试！");
            throw new ServiceException(JsonResult.State.ERR_INTERNAL_ERROR, "服务器忙，请稍后再次尝试！");
        }
    }

    @Override
    public String login(AdminLoginDTO adminLoginDTO) {
        log.debug("准备认证数据：{}", adminLoginDTO);

        //执行认证 会将用户信息保存在UserDetails中  一定要返回UserDetails后续 其他请求的认证和授权还需要security来实现
        UserUserDetails userDetails =
                (UserUserDetails) userDetailsService.loadUserByUsername(adminLoginDTO.getUsername());
        if (userDetails == null) {
            throw new ServiceException(JsonResult.State.ERR_BAD_REQUEST, "登录失败，用户名不存在！");
        }
        if (!globalPasswordEncoder.matches(adminLoginDTO.getPassword(), userDetails.getPassword())) {
            throw new ServiceException(JsonResult.State.ERR_BAD_REQUEST, "登录失败，用户名或密码错误");
        }

        //如果程序可执行到此处。表示认证成功
        log.debug("userDetails返回结果的id:{}", userDetails.getId());
        log.debug("userDetails返回结果的用户名：{}", userDetails.getUsername());
        log.debug("userDetails返回结果的权限信息：{}", userDetails.getAuthorities());

        //根据Spring Security返回的用户数据 生成jwt数据
        Map<String, Object> claims = new HashMap<>();
        claims.put("id", userDetails.getId());
        claims.put("username", userDetails.getUsername());
        //保存登录用户的权限信息 到jwt中
        //JSON.toJSONString(Object o)将对象0转换为json格式的字符串
        claims.put("permissions", JSON.toJSONString(userDetails.getAuthorities()));

        String jwt =
                Jwts.builder()
                        .setHeaderParam(Header.TYPE, Header.JWT_TYPE)
                        .setHeaderParam(Header.CONTENT_TYPE, "HS256")
                        .setClaims(claims)
                        .setExpiration(new Date(System.currentTimeMillis() + expiration * 1000)) //jwt数据是 7天内有效  参数值就是 5小时后的时间
                        .signWith(SignatureAlgorithm.HS256, secretKey)
                        .compact();
        log.debug("返回JWT数据：{}", jwt);
        return jwt;
    }

    @Override
    public Admin getById(Long id) {
        return adminMapper.getById(id);
    }
}
